﻿using System.Collections.Generic;
using System.Threading;
using System;

namespace PortableTPL
{
    internal class WorkerThread : IDisposable
    {
        readonly Thread thread;
        readonly Queue<Task> workQueue = new Queue<Task>();
        readonly ManualResetEvent workEvent = new ManualResetEvent(false);
        readonly StandardTaskScheduler scheduler;
        bool quitCondition;

        internal WorkerThread(int index, StandardTaskScheduler scheduler)
        {
            this.scheduler = scheduler;
            thread = new Thread(ThreadJob) { IsBackground = true, Name = "TPL Worker " + index };

            thread.Start();
        }

        internal void AddTask(Task task)
        {
            task.SetIsScheduled();

            lock (workQueue)
            {
                workQueue.Enqueue(task);
            }
            workEvent.Set();
        }

        internal void AddTasks(Task[] tasks)
        {
            lock (workQueue)
            {
                foreach (Task task in tasks)
                {
                    workQueue.Enqueue(task);
                }
            }
            workEvent.Set();
        }

        internal bool TryDequeue(out Task result)
        {
            return workQueue.TryDequeue(out result);
        }

        internal int QueueLength
        {
            get
            {
                lock (workQueue)
                {
                    return workQueue.Count;
                }
            }
        }

        void ThreadJob()
        {
            while (true)
            {
                workEvent.WaitOne();
                workEvent.Reset();

                if (quitCondition)
                    return;

                Task task;
                bool noWork = false;

                do
                {
                    bool emptyQueue = !workQueue.TryDequeue(out task);

                    if (emptyQueue && !scheduler.StealWork(this))
                    {
                        noWork = true;
                    }

                    if (task != null)
                        task.DoWork();

                    if (quitCondition)
                        return;

                }
                while (!noWork);
            }
        }

        public void Dispose()
        {
            quitCondition = true;
            workEvent.Set();
        }
    }
}
